/*
 * Code taken from the commit message in the patch for CVE-2016-10044
 * 22f6b4d34fcf039c63a94e7670e0da24f8575a5a
 * Original author: Jann Horn <jann@thejh.net>
 * Changed by Po-Hsu Lin <po-hsu.lin@canonical.com>
 * Modified to add it into the autotest framework.
 *
 * This CVE issue will let do_mmap() implicitly make AIO memory mappings
 * executable if the READ_IMPLIES_EXEC personality flag is set.
 * Therefore in the output, "rw-s" is good, "rwxs" is bad.
 */
#define _GNU_SOURCE
#include <inttypes.h>
#include <unistd.h>
#include <sys/personality.h>
#include <linux/aio_abi.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/syscall.h>
#include <string.h>

#define exception(msg){\
    perror(msg);\
    exit(EXIT_FAILURE);\
}
int main(void) {
    char fname[512];
    char buf[512];
    int ret = EXIT_SUCCESS;
    FILE *fptr;
    personality(READ_IMPLIES_EXEC);
    aio_context_t ctx = 0;
    if (syscall(__NR_io_setup, 1, &ctx)) {
        exception("io_setup");
    }
    sprintf(fname, "/proc/%d/maps", (int)getpid());
    if ((fptr = fopen(fname, "r")) == NULL) {
        exception(fname);
    }
    else {
        while (fgets(buf, sizeof(buf), fptr) != NULL) {
            uint64_t vm_start;
            uint64_t vm_end;
            char rwxs[5] = {0};
            uint64_t pgoff;
            int32_t major, minor;
            uint32_t ino;
            char proc[7] = {0};
            int32_t n;
            n = sscanf(buf, "%"PRIx64"-%"PRIx64" %4s %"PRIx64" %x:%x %u %6s",
                       &vm_start,
                       &vm_end,
                       rwxs,
                       &pgoff,
                       &major, &minor,
                       &ino,
                       proc);
            if (n < 7) {
                fprintf(stderr, "unexpected line: %s\n", buf);
                continue;
            }
            if (strcmp("/[aio]", proc) == 0) {
                printf("Permission for /[aio] is %s, expecting rw-s\n", rwxs);
                if (strcmp("rw-s", rwxs) != 0) {
                    fprintf(stderr, "FAILED. Vulnerable to CVE-2016-10044\n");
                    ret = EXIT_FAILURE;
                }
                else {
                    printf("PASSED\n");
                }
                break;
            }
        };
        fclose(fptr);
    };
    return ret;
};
